package com.timecard.client;

import java.util.Date;
import java.util.Vector;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DecoratedTabPanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTMLTable;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.datepicker.client.DateBox;
import com.timecard.client.dto.DataService;
import com.timecard.client.dto.DataServiceAsync;
import com.timecard.client.dto.TimeEntryData;
import com.timecard.client.login.LoginInfo;
import com.timecard.client.login.LoginService;
import com.timecard.client.login.LoginServiceAsync;
import com.timecard.client.login.exception.NotLoggedInException;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Timecard implements EntryPoint {

	private VerticalPanel mainPanel = new VerticalPanel();
	private AbsolutePanel totalPanel = new AbsolutePanel();
	private DockPanel navPanel = new DockPanel();
	private HorizontalPanel topPanel = new HorizontalPanel();

	private Label totalLabel = new Label("0.00");
	private FlexTable flexEntryTable = new FlexTable();
	private FlexTable flexCurrentTable = new FlexTable();
	private Image logo = new Image();

	private Date startDate;
	private int currentRow = 0;
	private int currentColumn = 0;

	private LoginInfo loginInfo;

	private final DataServiceAsync dataService = GWT.create(DataService.class);
	private DecoratedTabPanel tabPanel;

	private Anchor sortByProject;
	private Anchor sortByMilestone;
	private Anchor sortByBillable;
	private Anchor sortByDate;
	@Override
	public void onModuleLoad() {
		logo.setUrl("images/ae_gwt_java2.png");
		LoginServiceAsync loginService = GWT.create(LoginService.class);
		loginService.login(GWT.getHostPageBaseURL(), new AsyncCallback<LoginInfo>() {

			@Override
			public void onSuccess(LoginInfo result) {
				loginInfo = result;
				if(loginInfo.isLoogedIn()) {
					loadMainUI();
				} else {
					loadLoginUI();
				}
			}

			@Override
			public void onFailure(Throwable caught) {

			}
		});
	}

	private void loadLoginUI() {
		VerticalPanel loginPanel = new VerticalPanel();
		Anchor loginLink = new Anchor("Sign In");
		loginLink.setHref(loginInfo.getLoginUrl());
		loginPanel.add(logo);
		loginPanel.add(new Label("Please sign-in with your Google Account to" +	"access Time Entry application."));
		loginPanel.add(loginLink);
		RootPanel.get("timeEntryUI").add(loginPanel);
	}

	private void loadMainUI() {
		logo.setUrl("images/ae_gwt_java2.png");

		HorizontalPanel userPanel = new HorizontalPanel();
		Anchor logOutLink = new Anchor("Sign Out");
		logOutLink.setHref(loginInfo.getLogoutUrl());
		Label separator = new Label("|");

		separator.setStyleName("separator");
		userPanel.add(new Label(loginInfo.getEmailAddress()));
		userPanel.add(separator);
		userPanel.add(logOutLink);

		topPanel.setWidth("1000px");
		topPanel.add(logo);
		topPanel.add(userPanel);
		topPanel.setCellHorizontalAlignment(userPanel,
				HasHorizontalAlignment.ALIGN_RIGHT);

		//set up a horizontal panel to hold the date picker
		HorizontalPanel leftNav = new HorizontalPanel();
		leftNav.setSpacing(5);
		leftNav.add(new Label("Week Start Date"));
		DateBox dateBox = new DateBox();
		dateBox.setWidth("100px");
		dateBox.setFormat(new DateBox.DefaultFormat(DateTimeFormat.getFormat("M/d/yyyy")));
		leftNav.add(dateBox);

		//set up another horizontal panel to hold the Add and Save buttons
		HorizontalPanel buttonPanel = new HorizontalPanel();
		buttonPanel.setSpacing(5);
		Button addRowButton = new Button("Add Row");
		Button saveButton = new Button("Save");

		saveButton.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				saveEntries();
				removeAllRows();
			}
		});
		buttonPanel.add(addRowButton);
		buttonPanel.add(saveButton);

		//set up another horizontal panel to dock all of the buttons to the right
		final HorizontalPanel rightNav = new HorizontalPanel();
		rightNav.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
		rightNav.setWidth("100%");
		rightNav.add(buttonPanel);

		//add all of the navigation panel to the dock panel
		navPanel.setWidth("1000px");
		navPanel.add(leftNav, DockPanel.WEST);
		navPanel.add(rightNav, DockPanel.EAST);

		//setup a horizontal panel to hold the grand total
		totalPanel.setSize("1000px", "50px");
		totalPanel.add(new Label("Total:"), 900, 25);
		totalPanel.add(totalLabel, 950, 25);

		//listen for mouse events on the add new row button
		addRowButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				addRow();
			}
		});

		//listen for the changes in the value of the date
		dateBox.addValueChangeHandler(new ValueChangeHandler<Date>() {
			@Override
			public void onValueChange(ValueChangeEvent<Date> event) {
				startDate = event.getValue();
				renameColumns();
				//show the main parts of the UI now
				flexEntryTable.setVisible(true);
				rightNav.setVisible(true);
				totalPanel.setVisible(true);
			}
		});

		//setup the width of the table to expand the size of the navpanel
		flexEntryTable.setWidth("100%");
		//set the style for the table to be accessed in the css
		flexEntryTable.setStylePrimaryName("timeEntryTable");

		//add the columns and headers
		flexEntryTable.setText(0, 0, "Project");
		flexEntryTable.setText(0, 1, "Milestone");
		flexEntryTable.setText(0, 2, "Billable");
		flexEntryTable.setText(0, 3, "Mon");
		flexEntryTable.setText(0, 4, "Tue");
		flexEntryTable.setText(0, 5, "Wed");
		flexEntryTable.setText(0, 6, "Thu");
		flexEntryTable.setText(0, 7, "Fri");
		flexEntryTable.setText(0, 8, "Sat");
		flexEntryTable.setText(0, 9, "Sun");
		flexEntryTable.setText(0, 10, "Total");

		VerticalPanel tab1Content = new VerticalPanel();
		tab1Content.add(navPanel);
		tab1Content.add(flexEntryTable);
		tab1Content.add(totalPanel);

		tabPanel = new DecoratedTabPanel();
		tabPanel.setWidth("100%");
		tabPanel.setAnimationEnabled(true);
		tabPanel.add(tab1Content, "Enter Time");
		tabPanel.add(flexCurrentTable, "Current Entries");
		tabPanel.selectTab(0);

		//add the navpanel and flex table to the main panel
		mainPanel.add(topPanel);
		mainPanel.add(tabPanel);

		//associate the main panel with the HTML host page
		RootPanel.get("timeEntryUI").add(mainPanel);
		addRow();
		getCurrentEntries("date");


	}

	protected void renameColumns() {
		flexEntryTable.setText(0, 3, formatDate(startDate));
		flexEntryTable.setText(0, 4, formatDate(addDays(startDate, 1)));
		flexEntryTable.setText(0, 5, formatDate(addDays(startDate, 2)));
		flexEntryTable.setText(0, 6, formatDate(addDays(startDate, 3)));
		flexEntryTable.setText(0, 7, formatDate(addDays(startDate, 4)));
		flexEntryTable.setText(0, 8, formatDate(addDays(startDate, 5)));
		flexEntryTable.setText(0, 9, formatDate(addDays(startDate, 6)));
	}

	@SuppressWarnings("deprecation")
	private Date addDays(Date d, int numberOfDays) {
		int day = d.getDate();
		int month = d.getMonth();
		int year = d.getYear();
		return new Date(year, month, day+numberOfDays);
	}

	@SuppressWarnings("deprecation")
	private String formatDate(Date d) {
		return (d.getMonth()+1)+"/"+d.getDate()+"("+d.toString().substring(0, 2)+")";
	}

	private void addRow() {
		int row = flexEntryTable.getRowCount();

		final ListBox lbMilestones = new ListBox(false);
		final ListBox lbProjects = new ListBox(false);
		lbProjects.addItem("--Select a Project --");

		//create the time input fields for all 7 days
		final TextBox day1 = new TextBox();
		day1.setValue("0");
		day1.setWidth("50px");
		day1.setEnabled(false);
		day1.addValueChangeHandler(timeChangeHandler);

		final TextBox day2 = new TextBox();
		day2.setValue("0");
		day2.setWidth("50px");
		day2.setEnabled(false);
		day2.addValueChangeHandler(timeChangeHandler);

		final TextBox day3 = new TextBox();
		day3.setValue("0");
		day3.setWidth("50px");
		day3.setEnabled(false);
		day3.addValueChangeHandler(timeChangeHandler);

		final TextBox day4 = new TextBox();
		day4.setValue("0");
		day4.setWidth("50px");
		day4.setEnabled(false);
		day4.addValueChangeHandler(timeChangeHandler);

		final TextBox day5 = new TextBox();
		day5.setValue("0");
		day5.setWidth("50px");
		day5.setEnabled(false);
		day5.addValueChangeHandler(timeChangeHandler);

		final TextBox day6 = new TextBox();
		day6.setValue("0");
		day6.setWidth("50px");
		day6.setEnabled(false);
		day6.addValueChangeHandler(timeChangeHandler);

		final TextBox day7 = new TextBox();
		day7.setValue("0");
		day7.setWidth("50px");
		day7.setEnabled(false);
		day7.addValueChangeHandler(timeChangeHandler);

		flexEntryTable.setWidget(row, 0, lbProjects);
		flexEntryTable.setWidget(row, 1, lbMilestones);
		flexEntryTable.setWidget(row, 2, new CheckBox());
		flexEntryTable.setWidget(row, 3, day1);
		flexEntryTable.setWidget(row, 4, day2);
		flexEntryTable.setWidget(row, 5, day3);
		flexEntryTable.setWidget(row, 6, day4);
		flexEntryTable.setWidget(row, 7, day5);
		flexEntryTable.setWidget(row, 8, day6);
		flexEntryTable.setWidget(row, 9, day7);
		flexEntryTable.setWidget(row, 10, new Label("0.00"));

		flexEntryTable.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				HTMLTable.Cell cellForEvent = flexEntryTable.getCellForEvent(event);
				currentRow = cellForEvent.getRowIndex();
				currentColumn = cellForEvent.getCellIndex();
			}
		});

		dataService.getProjects(new AsyncCallback<String[]>() {

			@Override
			public void onFailure(Throwable caught) {
				handleError(caught);
			}

			@Override
			public void onSuccess(String[] result) {
				for (int i=0; i < result.length; i++)
					lbProjects.addItem(result[i]);
			}

		});
		lbProjects.addChangeHandler(new ChangeHandler() {

			@Override
			public void onChange(ChangeEvent event) {
				//remove all of the current items in the milstone list.
				for (int i = lbMilestones.getItemCount()-1; i>=0; i--)
					lbMilestones.removeItem(i);
				//get all of the milstones for the project
				dataService.getMilestones(lbProjects.getItemText(lbProjects.getSelectedIndex()), new AsyncCallback<String[]>() {

					@Override
					public void onFailure(Throwable caught) {
						handleError(caught);						
					}


					@Override
					public void onSuccess(String[] result) {
						for(int i = 0; i < result.length; i++) {
							lbMilestones.addItem(result[i]);
						}
						day1.setEnabled(true);
						day2.setEnabled(true);
						day3.setEnabled(true);
						day4.setEnabled(true);
						day5.setEnabled(true);
						day6.setEnabled(true);
						day7.setEnabled(true);
					}
				});
			}
		});
	}

	private ValueChangeHandler<String> timeChangeHandler = new ValueChangeHandler<String>() {
		@Override
		public void onValueChange(ValueChangeEvent<String> event) {
			try {
				double t = Double.parseDouble(event.getValue());
				if(t > 24) {
					Window.alert("You cannot work more than 24 hours a day");
				} else {
					totalRow();
				}
			} catch (NumberFormatException e) {
				TextBox tb = (TextBox)flexEntryTable.getWidget(currentRow, currentColumn);
				tb.setValue("0");
				flexEntryTable.setWidget(currentRow, currentColumn, tb);
				Window.alert("Not a valid number");
			}
		}
	};

	private void handleError(Throwable caught) {
		Window.alert(caught.getMessage());
		if(caught instanceof NotLoggedInException)
			Window.Location.replace(loginInfo.getLogoutUrl());
	}
	protected void totalRow() {
		double rowTotal = 0.00;
		for (int cell = 3; cell <= 9; cell++) {
			TextBox timeWidget = (TextBox)flexEntryTable.getWidget(currentRow, cell);
			double t = Double.parseDouble(timeWidget.getValue());
			rowTotal = rowTotal + t;
		}
		flexEntryTable.setWidget(currentRow, 10, new Label(NumberFormat.getFormat(".00").format(rowTotal)));
		totalGrid();
	}

	private void totalGrid() {
		double grandTotal = 0.00;
		for (int row = 1; row < flexEntryTable.getRowCount(); row++) {
			Label rowTotalWidget = (Label)flexEntryTable.getWidget(row, 10);
			double rowTotal = Double.parseDouble(rowTotalWidget.getText());
			grandTotal = grandTotal + rowTotal;
		};
		totalLabel.setText(NumberFormat.getFormat(".00").format(grandTotal));
	}

	private void saveEntries() {
		Vector<TimeEntryData> entries = new Vector<TimeEntryData>();

		for(int row = 1; row < flexEntryTable.getRowCount(); row++) {
			ListBox projectWidget = (ListBox) flexEntryTable.getWidget(row, 0);
			ListBox milestonesWidget = (ListBox) flexEntryTable.getWidget(row, 1);
			CheckBox billableWidget = (CheckBox) flexEntryTable.getWidget(row, 2);


			for(int column = 3; column < 10; column++) {
				TextBox textBox = (TextBox)flexEntryTable.getWidget(row, column);
				double hours = Double.parseDouble(textBox.getValue());
				if(hours > 0) {
					TimeEntryData ted = new TimeEntryData();
					ted.setHours(hours);
					ted.setMilestone(milestonesWidget.getItemText(milestonesWidget.getSelectedIndex()));
					ted.setProject(projectWidget.getItemText(projectWidget.getSelectedIndex()));
					ted.setBillable(billableWidget.getValue());
					ted.setDate(addDays(startDate, (column-3)));
					entries.add(ted);
				}
			}
		}
		if(!entries.isEmpty()) {
			// submit the entries to the server
			dataService.addEntries(entries, new AsyncCallback<String>() {

				@Override
				public void onFailure(Throwable caught) {
					handleError(caught);
				}

				@Override
				public void onSuccess(String result) {
					Window.alert(result);
					getCurrentEntries("date");
				}

			});
		}
	}

	private void removeAllRows() {
		// remove all of the rows from the flex table
		for(int row = flexEntryTable.getRowCount()-1; row > 0; row--) {
			flexEntryTable.removeRow(row);
		}

		//rest the total
		totalLabel.setText("0.00");
		//add a new blank row to the flex table
		addRow();
	}

	public void getCurrentEntries(String order) {
		//get all of the milestones for the project
		dataService.getEntries(order, new AsyncCallback<Vector<TimeEntryData>>() {
			
			@Override
			public void onFailure(Throwable caught) {
				handleError(caught);
			}

			@Override
			public void onSuccess(Vector<TimeEntryData> result) {
				int row = flexEntryTable.getRowCount();
				sortByProject = new Anchor("Project");
				sortByMilestone = new Anchor("Milestone");
				sortByBillable = new Anchor("Billable");
				sortByDate = new Anchor("Date");
				
				sortByProject.addClickHandler(new ClickHandler() {
					@Override
					public void onClick(ClickEvent event) {
						getCurrentEntries("project");
					}
				});
				sortByMilestone.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						getCurrentEntries("milestone");
					}
				});
				sortByDate.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						getCurrentEntries("date");
					}
				});

				sortByBillable.addClickHandler(new ClickHandler() {

					@Override
					public void onClick(ClickEvent event) {
						getCurrentEntries("billable");
					}
				});

				flexCurrentTable.setWidget(0, 0, sortByProject);
				flexCurrentTable.setWidget(0, 1, sortByMilestone);
				flexCurrentTable.setWidget(0, 2, sortByBillable);
				flexCurrentTable.setWidget(0, 3, sortByDate);

				for(TimeEntryData ted: result) {
					row++;
					flexCurrentTable.setText(row, 0, ted.getProject());
					flexCurrentTable.setText(row, 1, ted.getMilestone());
					flexCurrentTable.setText(row, 2, ted.getBillable() ? "Yes" : "No");
					flexCurrentTable.setText(row, 3, DateTimeFormat.getShortDateFormat().format(ted.getDate()));
					flexCurrentTable.setText(row, 4, String.valueOf(NumberFormat.getFormat(".00").format(ted.getHours())));

				}
			}

		});
	}
}






















